# -*- coding: utf-8 -*-
__author__ = 'lztkdr'
__date__ = '2019/7/27 15:50'


# 伪造 UserAgent
from fake_useragent import UserAgent

# scrapy
from scrapy.http import Request
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

#导入常用基础模块包
import re, requests,time,datetime,urllib

#自定义工具util
from utils.proxy_helper import getProxyHost
from utils.js_util import decrypt_anti_info_cookie
from utils import util

# orm 相关
from orm.orm_base import ORM_BASE,ORM_Engine
from sqlalchemy.orm import  sessionmaker

# 通过 ORM_BASE.metadata.create_all(ORM_Engine) 创建对应的表
# Base.metadata.create_all(engine)  所在的python 文件里，所有要创建的表的实体类，必须得导进来！！！
from orm import car_crawl_orm


# items
from items import *

# scrapy 加载器
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst


class CarItemLoader(ItemLoader):
    """
    自定义ItemLoader
    """
    #设定获取到的值，默认只取第一个
    default_output_processor = TakeFirst()



class GuaziCrawlSpider(CrawlSpider):
    name = 'guazi_crawl'
    allowed_domains = ['www.guazi.com']
    start_urls = ['https://www.guazi.com/www/buy']

    # rules: url提取规则集合
    rules = (
        Rule(LinkExtractor(allow=(r"www/buy/")), follow=True),
        Rule(LinkExtractor(allow=(r"www/buy/o\d{1,}c-1/#bread")), follow=True),
        Rule(LinkExtractor(allow=(r"[a-z]{2,}/[a-z0-9]{17}.htm#.+")),callback = 'parse_car_detail', follow=True)
    )

    def __init__(self):
        super(GuaziCrawlSpider, self).__init__()

        self.orm_session = None
        try:
            #先删除表
            ORM_BASE.metadata.drop_all(ORM_Engine)
            #根据同步表
            ORM_BASE.metadata.create_all(ORM_Engine)
            #定义数据库会话session
            self.orm_session = sessionmaker(ORM_Engine)()
        except Exception as ex:
            print(f"数据库session设置出错。出错原因：{ex}")


        # CrawlSpider 爬取，只是用 Crawl 不使用 selenium
        self.mode = 1

        # 输入关键字
        self.keyword = "大众"
        #self.keyword = input("开始起动瓜子网爬虫,请输入要爬取的关键字:")
        print(f"开始起动瓜子网爬虫,请输入要爬取的关键字:{self.keyword}")

        # 对搜索 关键字 编码
        url_encoding_keyword = urllib.parse.quote(self.keyword)
        self.search_keyword_url = f"https://www.guazi.com/www/buy/_{url_encoding_keyword}/?input=1"
        self.start_urls[0] = self.search_keyword_url

        # 是否要 启用 代理
        self.enabled_proxy = True

        # 初始化默认值
        self.proxy_host = None
        self.proxies = None
        self.useragent = None
        self._antipas_cookie = None
        self.last_update_time = None # 最后更新 antipas_cookie 的时间

        if self.mode == 1:# 只使用 scrapy 爬取
            # 初始化 设置代理 以及 设置antipas_cookie
            self.init_proxy_antipas()

        if self.proxy_host:
            print(f"获取到代理：{self.proxy_host}")

        if self._antipas_cookie:
            print(f"antipas_cookie:{self._antipas_cookie}")

    @property
    def antipas_cookie(self):
        """
        维护 代理池、ua 池， 每个代理IP 关联要 antipas_cookie 同时进行更新
        :return:
        """
        cookie = self._antipas_cookie
        curr_time = datetime.datetime.now()
        if (curr_time - self.last_update_time).seconds > 50: #代理时效为1分钟，这里 快到失效期 则 更新
            self.init_proxy_antipas()
            cookie = self._antipas_cookie
            print("自动更换 proxy/useragent ......")
        return cookie


    def init_proxy_antipas(self):
        """
        访问第1次通过正则拿到变化的 anti_info 值并解密得到每次访问的 cookie 值
        :return:
        """
        while True:
            try:
                # 设置 userAgent
                self.useragent = UserAgent().chrome

                # 发现：第1次请求 和 其他继续的请求 必须是同一个 userAgent ！！！, 所以这里 和 scrapy 默认的 userAgent 相同，不需使用 fake_useragent
                first_headers = {
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                    'User-Agent': self.useragent
                }

                # 是否启用代理
                if self.enabled_proxy:
                    # 使用代理
                    self.proxy_host = getProxyHost()
                    if self.proxy_host:
                        self.proxies = {'http': "http://" + self.proxy_host, 'https': "https://" + self.proxy_host}

                # 获取 antipas的cookie
                resp = requests.get(self.search_keyword_url, params=None, proxies=self.proxies, headers=first_headers)
                anti_info = re.findall(r".*(var value=anti.*;)", resp.text)[0]
                cookie_info = decrypt_anti_info_cookie(anti_info)
                # 设置 antipas 的cookie
                self._antipas_cookie = cookie_info
                self.last_update_time = datetime.datetime.now()
                break
            except Exception as ex:
                print(ex)
                time.sleep(2)
                continue

    def parse_car_detail(self, response):
        '''
        解析 二手车详情
        :param response:
        :return:
        '''
        url = response.url  # 详情url
        try:
            # 使用自定义 CarItemLoader
            item_loader = CarItemLoader(item=CarItem_Crawl(), response=response)

            detail_id = util.create_fingerprint(response.url)

            item_loader.add_value("detail_url", response.url)
            item_loader.add_value("url_finger_print", detail_id)
            item_loader.add_css("title", ".product-textbox .titlebox::text")

            item_loader.add_css("licensing_time", ".basic-eleven .one .typebox::text")
            item_loader.add_css("mileage", ".basic-eleven .two .typebox::text")
            item_loader.add_css("displacement", ".basic-eleven .six .typebox::text")
            item_loader.add_css("gearbox", ".basic-eleven .five .typebox::text")

            img_src = response.css(".js-bigpic[data-index='0'] img:first-child::attr(src)").extract_first()
            item_loader.add_value("img_src", img_src)

            item_loader.add_css("sale_price", ".pricebox .pricestype::text")
            item_loader.add_css("year_check", ".basic-eleven .nine .typebox::text")
            item_loader.add_css("insurance", ".basic-eleven .ten .typebox::text")
            item_loader.add_css("store", ".baomai-right h3::text")
            item_loader.add_css("look_addr", ".baomai-right p::text")
            item_loader.add_css("owner", ".people-infor span[class='f20']::text")
            carItem_crawl = item_loader.load_item()
            print(carItem_crawl)

            # 图片数据下载，只下载每辆车的第一个图片,下载之后生成缩略图，数据更新 回 carItem_crawl
            #  CarImage 实体类 其实可以不用建立，直接在继承 的 ImagesPipeline 中 获取 img_src ,然后  yield scrapy.Request(img_src)
            #  只要注册了的 ImagesPipeline ，其他走 process_item 类型的 Pipeline ，也会走 ImagesPipeline！
            if img_src:
                image = CarImage(detail_id = detail_id,img_src = img_src)
                yield image

            # 车详情数据
            yield carItem_crawl

            print("---------------------------------------------------------------------------------------------------------------------------------------------------")
        except Exception as ex:
            print(f"parse_car_detail出现异常({url})：{ex}")
            req = Request(url,callback=self.parse_car_detail,dont_filter=True)
            yield req
